/*
 * @Author: 晟松
 * @Date: 2021-11-30 13:10:39
 * @LastEditors: 晟松
 * @LastEditTime: 2022-03-09 17:03:58
 * @FilePath: \mini-vue3d:\Code\node\koa2\volunteer-service\src\main\service\UserService.js
 * @Description: 对用户的一些操作，如login，getuserInfo,logout,register等
 */
import "module-alias/register";
import sequelize from "@main/db";
import { Op } from "sequelize";
import { isNull, isBlank } from "@main/util/ValidateUtil";
import { ParamError, LoginError, NotFoundError } from "@main/common/CommonError";
import { encryptPassword, checkPassword } from "@main/util/PasswordUtil";
import { studentSuffixAdd, residentSuffixAdd } from "@main/util/StrUtils";
import config from "@main/config";
import blacklist from "blacklist";
import whiteListed from "whitelisted";
// import jwt from "jsonwebtoken";
import { encrypt } from "@main/util/JwtUtils";
import enums from "@main/config/enums";
import { toEnumNum, toEnumStr } from "@main/util/EnumsUtil";

import Web3Service from "./Web3Service";

const userModel = sequelize.model("user");
const loginModel = sequelize.model("login");
const studentModel = sequelize.model("student");
const residentModel = sequelize.model("resident");
const schoolModel = sequelize.model("school");
const communityModel = sequelize.model("community");

// 获取所有定义的model
const models = sequelize.models;
// models:  true
// console.log("models: ", models.student == studentModel);

class UserService {
    /**
     * @description:
     * @param {*} username
     * @param {*} password
     * @param {*} identity 身份,一个enum值
     * @return {*}
     */
    static async login({ phone, password }) {
        if (isBlank(phone)) {
            throw new ParamError("用户名不能为空");
        }
        if (isBlank(password)) {
            throw new ParamError("密码不能为空");
        }
        let user = await loginModel.findOne({
            where: {
                phone: phone,
            },
            include: [studentModel, residentModel, schoolModel, communityModel],
        });
        if (!user) {
            throw new LoginError("账号或者密码错误");
        }
        // 获得原始值，而不是sequelize包装后的对象
        user = user.dataValues;
        console.log("user: ", user);

        let check = await checkPassword(password, user.password);
        if (!check) {
            throw new LoginError("账号或者密码错误");
        }
        // 将数据库数字identity转对字符可读形式
        user.identity = toEnumStr(user.identity, "identity");

        // 排除掉 password 字段
        let payload = blacklist(user, "password");
        let token = encrypt(payload);
        return { token, user: payload };
    }
    /**
     * @description:
     * @param {*} user 包含loginmodel的信息，还包含所要创建的用户的个人信息，如studentmodel或residentmodel的信息
     * @param isBatch 是否是批量注册的
     * @return {*}
     */
    static async create(user, isBatch = false) {
        console.log("user: ", user);
        // 判断一下手机号是否存在
        let phone = user.phone;
        let exsist = await models["login"].findOne({
            where: { phone },
        });
        // 没有的时候exsist为null
        if (exsist) {
            return {
                code: -1,
                text: "手机号已存在",
            };
        }
        // 此时identity为string
        let identity = user.identity;
        user.password = encryptPassword(user.password);
        // 此时identity为number，为了存进数据库
        user.identity = enums["identity"][identity];
        user.gender = enums["gender"][user.gender];

        // 白名单的使用，即只剩这几个字段
        let loginInfo = whiteListed(user, ["phone", "password", "identity"]);
        // 黑名单的使用，去除这几个
        let userInfo = blacklist(user, "phone", "password");

        // 注册还没审核的都为false
        if (!userInfo.available) userInfo.available = false;
        // 批量注册的改为true
        if (isBatch) userInfo.available = true;
        // 这里进行一些特殊处理，注意switch case后面跟了个 {} 保护作用域，使其不冲突
        switch (identity) {
            case "student": {
                userInfo.grade = toEnumNum(userInfo.grade, "grade");
                studentSuffixAdd(userInfo);

                // 学生需要将其个人信息发送给对应辅导员审核
                // 通过学校名和院系、年级查询对应辅导员
                let { schoolName, college, grade } = userInfo;

                // 似乎这里的学校，专业要做下模糊查询了，如湖南师范可以匹配到湖南师范大学的记录
                // 查询对应学院、年级的辅导员
                let counselor = await schoolModel.findOne({
                    where: {
                        schoolName,
                        college,
                        grade,
                    },
                });
                // console.log("counselor: ", counselor);
                // 利用 '短路' 的性质，第一个为false，则返回第二个进行判断，辅导员是否存在和可用
                if (isNull(counselor) || !counselor.available) {
                    throw new NotFoundError("暂无所对应学院的辅导员加入本系统");
                }
                // 对应外键赋值上去，且这个辅导员是可用的
                userInfo.schoolId = counselor.id;
                break;
            }

            case "resident": {
                // 居民需要将其个人信息发送给对应居委会审核
                residentSuffixAdd(userInfo);
                // 通过社区名进行匹配
                let { communityName } = userInfo;
                let neighborhoodCommittee = await communityModel.findOne({
                    where: {
                        communityName,
                    },
                });
                if (isNull(neighborhoodCommittee)) {
                    throw new NotFoundError("暂无所对应社区的居委会加入本系统");
                }
                // 对应外键赋值上去
                userInfo.communityId = neighborhoodCommittee.id;
                break;
            }
            case "school":
                {
                    // 这里的school特指院级辅导员的注册，校级管理员的注册由系统管理员进行操作
                    userInfo.managementLevel = enums["managementLevel"]["college"];
                    userInfo.grade = toEnumNum(userInfo.grade, "grade");
                    // 只需要学校
                    let { schoolName } = userInfo;
                    // 对应校级管理员
                    let schoolAdmin = await schoolModel.findOne({
                        where: {
                            schoolName,
                            managementLevel: enums.managementLevel["school"],
                        },
                    });

                    if (isNull(schoolAdmin)) {
                        throw new NotFoundError("暂无所对应学校的管理员加入本系统");
                    }
                    // 设置外键，只想对应的学校的管理员
                    userInfo.schoolId = schoolAdmin.id;
                }
                break;
            default:
        }
        // 将其身份信息挂载上去，方便在创建login信息是附带关联用户信息
        loginInfo[identity] = userInfo;
        console.log("loginInfo: ", loginInfo);

        // 创建关联
        try {
            // 使用事务进行处理,这个事务好像不能控制关联创建出错的情形
            const result = await sequelize.transaction(async (t) => {
                let res = await loginModel.create(loginInfo, {
                    include: [
                        {
                            association: loginModel[identity],
                        },
                    ],
                });

                // 存储一下到区块链中
                await Web3Service.updateOne("login", res.id);
                await Web3Service.updateOne(identity, res[identity].id);

                return res;
            });
            return result;
        } catch (error) {
            return error;
        }
    }
    /**
     * @description: 测试使用
     * @param {*} id
     * @return {*}
     */
    static async findById(id) {
        return await userModel.findByPk(id);
    }

    static async findUserInfo(params) {
        let { id, identity } = params;
        console.log("params: ", params);
        // identity = toEnumStr(identity, "identity");
        // console.log("通过主键查找：", await models[identity].findByPk(id));

        let userInfo = await models[identity].findOne({
            where: {
                id: id,
            },
            include: models["login"],
        });
        if (isNull(userInfo)) {
            throw new NotFoundError("不存在此数据");
        }
        let loginInfo = await models["login"].findOne({
            where: {
                id: userInfo.loginId,
            },
        });
        // 特别把phone也加上去返回
        userInfo = userInfo.dataValues;
        userInfo.phone = loginInfo.phone;
        console.log("userInfo.login: ", userInfo.dataValues, loginInfo);
        return userInfo;
    }

    static async updateById(user) {
        let { id, identity, phone } = user;
        console.log("identity: ", identity);
        user = blacklist(user, "identity", "phone");
        console.log("updateById: ", user);
        // 配合后端修改用户信息，将string转为number
        if (!isBlank(user.gender) && (user.gender == "male" || user.gender == "female")) {
            user.gender = toEnumNum(user.gender, "gender");
        }
        // 如果存在手机号的话
        if (!isBlank(phone)) {
            let user = await models[identity].findOne({
                where: {
                    id,
                },
                include: models["login"],
            });
            await loginModel.update(
                {
                    phone,
                },
                {
                    where: {
                        id: user.loginId,
                    },
                }
            );
        }
        let res = await models[identity].update(user, {
            where: {
                id,
            },
        });

        await Web3Service.updateOne(identity, id);
        return res;
    }
    /**
     * @description: 更新用户密码,被废除了，使用下面的 updatePwd
     * @param {*} params
     * @return {*}
     */
    static async updatePassword(params) {
        let { id, identity, password } = params;
        password = encryptPassword(password);
        let user = await models[identity].findByPk(id);

        let res = models["login"].update(
            {
                password: password,
            },
            {
                where: {
                    id: user.loginId,
                },
            }
        );
        await Web3Service.updateOne("login", user.loginId);
        return res;
    }
    /**
     * @description: 根据用户id删除记录，主要是删除login表中的数据和所在身份表中的数据，志愿服务表就先不管
     * @param {*} user
     * @return {*}
     */
    static async deleteById(params) {
        let { id, identity } = params;
        let user = await models[identity].findOne({
            where: {
                id,
            },
            include: models["login"],
        });
        await loginModel.destroy({
            where: {
                id: user.loginId,
            },
        });
        await models[identity].destroy({
            where: {
                id: id,
            },
        });
        return "删除成功";
    }
    /**
     * @description: 更新密码
     * @param {*} params
     * @return {*}
     */
    static async updatePwd(params) {
        let { id, identity, oldPwd, newPwd } = params;
        let user = await models[identity].findOne({
            where: {
                id,
            },
            include: models["login"],
        });

        let check = await checkPassword(oldPwd, user.login.password);
        if (!check) {
            throw new LoginError("密码错误");
        } else {
            let res = await loginModel.update(
                {
                    password: encryptPassword(newPwd),
                },
                {
                    where: {
                        id: user.loginId,
                    },
                }
            );

            // 存储一下到区块链中
            await Web3Service.updateOne("login", user.loginId);

            return res;
        }
    }
    /**
     * @description: 获取所有的社区名单
     * @param {*} params
     * @return {*}
     */
    static async getAllCommunity(params) {
        let allCommunity = await models["community"].findAll({
            attributes: ["communityName"],
        });
        let map = new Set();
        for (let community of allCommunity) {
            map.add(community.communityName);
        }
        return map;
    }

    /**
     * @description: 获取所有的社区详细信息
     * @param {*} params
     * @return {*}
     */
    static async getAllCommunityInfo(params) {
        let all = await models["community"].findAll({
            include: models["login"],
        });
        for (let i = 0; i < all.length; i++) {
            all[i].dataValues.phone = all[i].login.phone;
            // console.log(i);
        }
        return all;
    }
    /**
     * @description: 获取所有的学校名单
     * @param {*} params
     * @return {*}
     */
    static async getAllSchool(params) {
        let allSchool = await models["school"].findAll({
            attributes: ["schoolName", "college"],
        });
        console.log("allSchool: ", allSchool);
        let map = {};
        for (let school of allSchool) {
            if (map[school.schoolName]) {
                map[school.schoolName].add(school.college);
            } else {
                map[school.schoolName] = new Set();
            }
        }
        return map;
    }
    /**
     * @description: 获得所有学校的详细信息
     * @param {*} params
     * @return {*}
     */
    static async getAllSchoolInfo(params) {
        let allSchool = await models["school"].findAll({
            include: models["login"],
        });
        console.log("allSchool: ", allSchool);
        // 所有的校级管理员
        let allSchoolAdmin = [];
        for (let i = 0; i < allSchool.length; i++) {
            allSchool[i].dataValues.phone = allSchool[i].login.phone;

            if (allSchool[i].managementLevel == enums.managementLevel["school"]) {
                // belongs是所有属于自己学校的院级管理员
                allSchool[i].dataValues.belongs = [];
                allSchoolAdmin.push(allSchool[i].dataValues);
            }
            // console.log(i);
        }
        let map = {};
        for (let school of allSchool) {
            if (map[school.schoolName]) {
                map[school.schoolName].add(school.college);
            } else {
                map[school.schoolName] = new Set();
            }
        }

        // 然后再根据allSchoolAdmin形成一个树状结构,通过学校名和院级名进行层级排列
        for (let i = 0; i < allSchoolAdmin.length; i++) {
            // 获得校级管理员的id
            let { id } = allSchoolAdmin[i];
            for (let j = 0; j < allSchool.length; j++) {
                // 根据这个所属的school id进行判断
                let { schoolId } = allSchool[j];
                if (id == schoolId) {
                    allSchoolAdmin[i].belongs.push(allSchool[j]);
                }
            }
        }

        return { map, allSchool, allSchoolAdmin };
    }
    /**
     * @description: 获得所有学生和居民的积分排行
     * @param {*} params
     * @return {*}
     */
    static async getIntegralRank(params) {
        let { offset, limit } = params;
        let allStudent = await models["student"].findAll({
            where: {
                available: true,
            },
            order: [["timeIntegral", "DESC"]], //根据积分数进行降序排序
        });
        let allResident = await models["resident"].findAll({
            where: {
                available: true,
            },
            order: [["timeIntegral", "DESC"]], //根据积分数进行降序排序
        });
        return {
            allStudent,
            allResident,
        };
    }
}

export default UserService;
